/**
 * @file	
 * @author	chipsea
 * @brief	
 * @version	0.1
 * @date	2020-11-30
 * @copyright Copyright (c) 2020, CHIPSEA Co., Ltd.
 * @note
 */

/**************************************************************************************************
  Filename:       heartrateservice.c
  Revised:        
  Revision:       

  Description:    This file contains the Heart Rate sample service 
                  for use with the Heart Rate sample application.

**************************************************************************************************/

/*********************************************************************
 * INCLUDES
 */
#include "bcomdef.h"
#include "OSAL.h"
#include "linkdb.h"
#include "att.h"
#include "gap.h"
#include "gatt.h"
#include "gatt_uuid.h"
#include "gatt_profile_uuid.h"
#include "gattservapp.h"

#include "log.h"
#include "PulseOximeterservice.h"
#include "userApp.h"
/*********************************************************************
 * MACROS
 */

/*********************************************************************
 * CONSTANTS
 */

// Position of heart rate measurement value in attribute array
#define PLX_SPORTCHECK_VALUE_POS            2     //Indicate
#define PLX_CONTINUOUS_VALUE_POS            5     //Notify
#define PLX_RECORDACCESSCP_VALUE_POS        10    //Indicate
/*********************************************************************
 * TYPEDEFS
 */

/*********************************************************************
 * GLOBAL VARIABLES
 */
// Pulse Oximter service
CONST uint8 PulseOximterServiceUUID[ATT_BT_UUID_SIZE] =
{ 
  LO_UINT16(PulseOximeterServiceUUID), HI_UINT16(PulseOximeterServiceUUID)
};

// SpotCheckMeasure measurement characteristic
CONST uint8 PLX_SpotCheckMeasureCharacterUUID[ATT_BT_UUID_SIZE] =
{ 
  LO_UINT16(PLX_SpotCheckMeasurementUUID), HI_UINT16(PLX_SpotCheckMeasurementUUID)
};

// Sensor location characteristic
CONST uint8 PLX_ContinuousMeasureCharaterUUID[ATT_BT_UUID_SIZE] =
{ 
  LO_UINT16(PLX_ContinuousMeasurementUUID), HI_UINT16(PLX_ContinuousMeasurementUUID)
};

// Command characteristic
CONST uint8 PLX_FeaturesCharaterUUID[ATT_BT_UUID_SIZE] =
{ 
  LO_UINT16(PLX_FeaturesUUID), HI_UINT16(HEARTRATE_CTRL_PT_UUID)
};

CONST uint8 PLX_RecordAccessCPCharacter[ATT_BT_UUID_SIZE] =
{ 
  LO_UINT16(PLX_RecordAccessCP), HI_UINT16(PLX_RecordAccessCP)
};

/*********************************************************************
 * EXTERNAL VARIABLES
 */

/*********************************************************************
 * EXTERNAL FUNCTIONS
 */

/*********************************************************************
 * LOCAL VARIABLES
 */

static PLXPServiceCB_t PLXPServiceCB;

/*********************************************************************
 * Profile Attributes - variables
 */

// PulseOximeters Service attribute
static CONST gattAttrType_t PLXPService = { ATT_BT_UUID_SIZE, PulseOximterServiceUUID };

// PLX Spot-check Measurement Characteristic
// Note characteristic value is not stored here
static uint8 PLX_SpotCheckMeasureProps = GATT_PROP_INDICATE;
static SpO2SpotCheckFlag SpO2SpotCheck_Flag={
  .flagByte = 0,
};
static gattCharCfg_t PLX_SpotCheckMeasureClientCharCfg[GATT_MAX_NUM_CONN];

// PLX Continuous Measurement Characteristic
static uint8 PLX_ContinuousMeasureProps = GATT_PROP_NOTIFY;
static SpO2ContinuousMeasureFlag SpO2ContinuousMeasure_Flag={
  .flagByte = 0,
};
static gattCharCfg_t PLX_ContinuousMeasureClientCharCfg[GATT_MAX_NUM_CONN];

// PLX Features Characteristic
static uint8 PLX_FeaturesProps = GATT_PROP_READ;
static PLX_Features_Value_t PLXFeaturesValue;

// Record Access Control Point Characteristic
static uint8 RecordAccessCPProps = GATT_PROP_INDICATE|GATT_PROP_READ;
static uint8_t RecordAccessControlPoint = 0;
static gattCharCfg_t PLX_RecordAccessCpClientCharCfg[GATT_MAX_NUM_CONN];
/*********************************************************************
 * Profile Attributes - Table
 */

static gattAttribute_t PulseOximeterAttrTbl[] = 
{
  // pulse oximeter Service
  { 
    { ATT_BT_UUID_SIZE, primaryServiceUUID }, /* type */
    GATT_PERMIT_READ,                         /* permissions */
    0,                                        /* handle */
    (uint8 *)&PLXPService                     /* pValue */
  },

    // PLX Spot-check Measurement Characteristic Measurement Declaration
    { 
      { ATT_BT_UUID_SIZE, characterUUID },
      GATT_PERMIT_READ, 
      0,
      &PLX_SpotCheckMeasureProps 
    },

      // PLX Spot-check Measurement Value
      { 
        { ATT_BT_UUID_SIZE, PLX_SpotCheckMeasureCharacterUUID },
        0, 
        0, 
        (uint8 *)&SpO2SpotCheck_Flag.flagByte
      },

      // PLX Spot-check Measurement Client Characteristic Configuration
      { 
        { ATT_BT_UUID_SIZE, clientCharCfgUUID },
        GATT_PERMIT_READ | GATT_PERMIT_WRITE, 
        0, 
        (uint8 *) &PLX_SpotCheckMeasureClientCharCfg 
      },     

    // PLX Continuous Measurement Characteristic Declaration
    { 
      { ATT_BT_UUID_SIZE, characterUUID },
      GATT_PERMIT_READ, 
      0,
      &PLX_ContinuousMeasureProps 
    },

      // PLX Continuous Measurement Value
      { 
        { ATT_BT_UUID_SIZE, PLX_ContinuousMeasureCharaterUUID },
        0, 
        0, 
        (uint8 *)&SpO2ContinuousMeasure_Flag.flagByte
      },

      // PLX Continuous Measurement Client Characteristic Configuration
      { 
        { ATT_BT_UUID_SIZE, clientCharCfgUUID },
        GATT_PERMIT_READ | GATT_PERMIT_WRITE, 
        0, 
        (uint8 *) &PLX_ContinuousMeasureClientCharCfg 
      },  

    // PLX Features Characteristic Declaration
    { 
      { ATT_BT_UUID_SIZE, characterUUID },
      GATT_PERMIT_READ, 
      0,
      &PLX_FeaturesProps 
    },

      // PLX Continuous Measurement Value
      { 
        { ATT_BT_UUID_SIZE, PLX_FeaturesCharaterUUID },
        0, 
        0, 
        (uint8 *)&PLXFeaturesValue 
      },

    // Record Access Control Point Characteristic Declaration
    { 
      { ATT_BT_UUID_SIZE, characterUUID },
      GATT_PERMIT_READ, 
      0,
      &RecordAccessCPProps 
    },

      // Record Access Control Point Value
      { 
        { ATT_BT_UUID_SIZE, PLX_RecordAccessCPCharacter },
        GATT_PERMIT_READ, 
        0, 
        &RecordAccessControlPoint 
      },

      // Record Access Control Point Client Characteristic Configuration
      { 
        { ATT_BT_UUID_SIZE, clientCharCfgUUID },
        GATT_PERMIT_READ | GATT_PERMIT_WRITE, 
        0, 
        (uint8 *) &PLX_RecordAccessCpClientCharCfg 
      }, 
};


/*********************************************************************
 * LOCAL FUNCTIONS
 */
static bStatus_t PulseOximeter_ReadAttrCB( uint16 connHandle, gattAttribute_t *pAttr, 
                            uint8 *pValue, uint16 *pLen, uint16 offset, uint8 maxLen );
static bStatus_t PulseOximeter_WriteAttrCB( uint16 connHandle, gattAttribute_t *pAttr,
                                 uint8 *pValue, uint16 len, uint16 offset );

/*********************************************************************
 * PROFILE CALLBACKS
 */
// Heart Rate Service Callbacks
CONST gattServiceCBs_t PulseOximeterCBs =
{
  PulseOximeter_ReadAttrCB,   // Read callback function pointer
  PulseOximeter_WriteAttrCB,  // Write callback function pointer
  NULL                        // Authorization callback function pointer
};

/*********************************************************************
 * PUBLIC FUNCTIONS
 */

/*********************************************************************
 * @fn      HeartRate_AddService
 *
 * @brief   Initializes the Heart Rate service by registering
 *          GATT attributes with the GATT server.
 *
 * @param   services - services to add. This is a bit map and can
 *                     contain more than one service.
 *
 * @return  Success or Failure
 */
bStatus_t PulseOximeter_AddService( uint32_t services )
{
  uint8 status = SUCCESS;

  // Initialize Client Characteristic Configuration attributes
  // GATTServApp_InitCharCfg( INVALID_CONNHANDLE, PLX_SpotCheckMeasureClientCharCfg );
  // GATTServApp_InitCharCfg( INVALID_CONNHANDLE, PLX_ContinuousMeasureClientCharCfg );
  // GATTServApp_InitCharCfg( INVALID_CONNHANDLE, PLX_RecordAccessCpClientCharCfg );

  // if ( services & HEARTRATE_SERVICE )
  {
    // Register GATT attribute list and CBs with GATT Server App
    status = GATTServApp_RegisterService( PulseOximeterAttrTbl, 
                                          GATT_NUM_ATTRS( PulseOximeterAttrTbl ),
                                          &PulseOximeterCBs );
  }

  return ( status );
}

/*********************************************************************
 * @fn      PulseOximeter_Register
 *
 * @brief   Register a callback function with the PulseOximeter Service.
 *
 * @param   pfnServiceCB - Callback function.
 *
 * @return  None.
 */
void PulseOximeter_Register( PLXPServiceCB_t pfnServiceCB )
{
  PLXPServiceCB = pfnServiceCB;
}

/*********************************************************************
 * @fn      HeartRate_SetParameter
 *
 * @brief   Set a Heart Rate parameter.
 *
 * @param   param - Profile parameter ID
 * @param   len - length of data to right
 * @param   value - pointer to data to write.  This is dependent on
 *          the parameter ID and WILL be cast to the appropriate 
 *          data type (example: data type of uint16 will be cast to 
 *          uint16 pointer).
 *
 * @return  bStatus_t
 */
bStatus_t PulseOximeter_SetParameter( uint8 param, uint8 len, void *value )
{
  bStatus_t ret = SUCCESS;
  // switch ( param )
  // {
  //    case HEARTRATE_MEAS_CHAR_CFG:
  //     // Need connection handle
  //     //heartRateMeasClientCharCfg.value = *((uint16*)value);
  //     break;      

  //   case HEARTRATE_SENS_LOC:
  //     heartRateSensLoc = *((uint8*)value);
  //     break;

  //   default:
  //     ret = INVALIDPARAMETER;
  //     break;
  // }
  
  return ( ret );
}

/*********************************************************************
 * @fn      HeartRate_GetParameter
 *
 * @brief   Get a Heart Rate parameter.
 *
 * @param   param - Profile parameter ID
 * @param   value - pointer to data to get.  This is dependent on
 *          the parameter ID and WILL be cast to the appropriate 
 *          data type (example: data type of uint16 will be cast to 
 *          uint16 pointer).
 *
 * @return  bStatus_t
 */
bStatus_t PulseOximeter_GetParameter( uint8 param, void *value )
{
  bStatus_t ret = SUCCESS;
  // switch ( param )
  // {
  //   case HEARTRATE_MEAS_CHAR_CFG:
  //     // Need connection handle
  //     //*((uint16*)value) = heartRateMeasClientCharCfg.value;
  //     break;      

  //   case HEARTRATE_SENS_LOC:
  //     *((uint8*)value) = heartRateSensLoc;
  //     break;
      
  //   case HEARTRATE_COMMAND:
  //     *((uint8*)value) = heartRateCommand;
  //     break;  

  //   default:
  //     ret = INVALIDPARAMETER;
  //     break;
  // }
  
  return ( ret );
}

/*********************************************************************
 * @fn          HeartRate_MeasNotify
 *
 * @brief       Send a notification containing a heart rate
 *              measurement.
 *
 * @param       connHandle - connection handle
 * @param       pNoti - pointer to notification structure
 *
 * @return      Success or Failure
 */
bStatus_t PulseOximeter_ContiunusMeasNotify( uint16 connHandle, attHandleValueNoti_t *pNoti )
{
  uint16 value = GATTServApp_ReadCharCfg( connHandle, PLX_ContinuousMeasureClientCharCfg );

  // If notifications enabled
  if ( value & GATT_CLIENT_CFG_NOTIFY )
  {
    // Set the handle
    pNoti->handle = PulseOximeterAttrTbl[PLX_CONTINUOUS_VALUE_POS].handle;
  
    // Send the notification
    return GATT_Notification( connHandle, pNoti, FALSE );
  }

  return bleIncorrectMode;
}


bStatus_t PulseOximeter_SpotCheckMeasIndicate( uint16 connHandle, attHandleValueInd_t *pInidicate )
{
  uint16 value = GATTServApp_ReadCharCfg( connHandle, PLX_SpotCheckMeasureClientCharCfg );

  // If notifications enabled
  if ( value & GATT_CLIENT_CFG_INDICATE )
  {
    // Set the handle
    pInidicate->handle = PulseOximeterAttrTbl[PLX_SPORTCHECK_VALUE_POS].handle;
  
    // Send the notification
    return GATT_Indication( connHandle, pInidicate, FALSE,UseAPP_TaskID);
  }

  return bleIncorrectMode;
}
/*********************************************************************
 * @fn          PulseOximeter_ReadAttrCB
 *
 * @brief       Read an attribute.
 *
 * @param       connHandle - connection message was received on
 * @param       pAttr - pointer to attribute
 * @param       pValue - pointer to data to be read
 * @param       pLen - length of data to be read
 * @param       offset - offset of the first octet to be read
 * @param       maxLen - maximum length of data to be read
 *
 * @return      Success or Failure
 */
static bStatus_t PulseOximeter_ReadAttrCB( uint16 connHandle, gattAttribute_t *pAttr, 
                            uint8 *pValue, uint16 *pLen, uint16 offset, uint8 maxLen )
{
  bStatus_t status = SUCCESS;

  // Make sure it's not a blob operation (no attributes in the profile are long)
  if ( offset > 0 )
  {
    return ( ATT_ERR_ATTR_NOT_LONG );
  }
 
  uint16 uuid = BUILD_UINT16( pAttr->type.uuid[0], pAttr->type.uuid[1]);

  LOG("Read UUID = %X-----",uuid);

  if (uuid == PLX_RecordAccessCP)
  {
    *pLen = 1;
    pValue[0] = *pAttr->pValue;
  }
  else
  {
    status = ATT_ERR_ATTR_NOT_FOUND;
  }

  return ( status );
}

/*********************************************************************
 * @fn      PulseOximeter_WriteAttrCB
 *
 * @brief   Validate attribute data prior to a write operation
 *
 * @param   connHandle - connection message was received on
 * @param   pAttr - pointer to attribute
 * @param   pValue - pointer to data to be written
 * @param   len - length of data
 * @param   offset - offset of the first octet to be written
 *
 * @return  Success or Failure
 */
static bStatus_t PulseOximeter_WriteAttrCB( uint16 connHandle, gattAttribute_t *pAttr,
                                 uint8 *pValue, uint16 len, uint16 offset )
{
  bStatus_t status = SUCCESS;
 
  uint16 uuid = BUILD_UINT16( pAttr->type.uuid[0], pAttr->type.uuid[1]);
  LOG("Write UUID = %X-----",uuid);
  my_dump_byte(pValue,len);

  switch ( uuid )
  {
    // case HEARTRATE_CTRL_PT_UUID:
    //   if ( offset > 0 )
    //   {
    //     status = ATT_ERR_ATTR_NOT_LONG;
    //   }
    //   else if (len != 1)
    //   {
    //     status = ATT_ERR_INVALID_VALUE_SIZE;
    //   }
    //   else if (*pValue != HEARTRATE_COMMAND_ENERGY_EXP)
    //   {
    //     status = HEARTRATE_ERR_NOT_SUP;
    //   }
    //   else
    //   {
    //     *(pAttr->pValue) = pValue[0];
        
    //     (*PLXPServiceCB)(HEARTRATE_COMMAND_SET);
        
    //   }
    //   break;

    case GATT_CLIENT_CHAR_CFG_UUID:
      status = GATTServApp_ProcessCCCWriteReq( connHandle, pAttr, pValue, len,
                                               offset, GATT_CLIENT_CFG_NOTIFY|GATT_CLIENT_CFG_INDICATE);
      if ( status == SUCCESS )
      {
        uint16 charCfg = BUILD_UINT16( pValue[0], pValue[1] );

        // (*PLXPServiceCB)( (charCfg == GATT_CFG_NO_OPERATION) ?
        //                         HEARTRATE_MEAS_NOTI_DISABLED :
        //                         HEARTRATE_MEAS_NOTI_ENABLED );
      }
      break;       
 
    default:
      status = ATT_ERR_ATTR_NOT_FOUND;
      break;
  }

  return ( status );
}

/*********************************************************************
 * @fn          PulseOximeter_HandleConnStatusCB
 *
 * @brief       PulseOximeter Service link status change handler function.
 *
 * @param       connHandle - connection handle
 * @param       changeType - type of change
 *
 * @return      none
 */
void PulseOximeter_HandleConnStatusCB( uint16 connHandle, uint8 changeType )
{ 
  // Make sure this is not loopback connection
  if ( connHandle != LOOPBACK_CONNHANDLE )
  {
    // Reset Client Char Config if connection has dropped
    if ( ( changeType == LINKDB_STATUS_UPDATE_REMOVED )      ||
         ( ( changeType == LINKDB_STATUS_UPDATE_STATEFLAGS ) && 
           ( !linkDB_Up( connHandle ) ) ) )
    { 
      // GATTServApp_InitCharCfg( connHandle, PLX_SpotCheckMeasureClientCharCfg );
      // GATTServApp_InitCharCfg( connHandle, PLX_ContinuousMeasureClientCharCfg );
      // GATTServApp_InitCharCfg( connHandle, PLX_RecordAccessCpClientCharCfg );
    }
  }
}

/**
* @fn      	bStatus_t GAPRole_EncodeAdvServiceUUID(uint8_t * pAdvData, uint8_t * pOffset, uint8_t maxSize, uint8_t *uuid, uint8_t uuidLen)
* @brief    Encode advdata about device name
* @param    pAdvData - advdata buffer
*			pOffset - encode data offset in advdata buffer
*			maxSize - maximum size for advdata buffer
* 			devName - device name
*			devNameLen - device name length
* @return   none.
*/
bStatus_t GAPRole_EncodeAdvService16bitUUID(uint8_t * pAdvData, uint8_t * pOffset, uint8_t maxSize, uint16_t uuid)
{
	uint8_t offset;
	offset = *pOffset;
  uint8_t uuidLen = 2;
	if(offset + 2 + uuidLen > maxSize)
	{
		return bleInvalidRange;
	}
	pAdvData[offset++] = 1+uuidLen;
	pAdvData[offset++] = GAP_ADTYPE_16BIT_MORE;
	// osal_memcpy(&pAdvData[offset], uuid, uuidLen);
  pAdvData[offset++] = LO_UINT16(uuid);
  pAdvData[offset++] = HI_UINT16(uuid);
	*pOffset = offset;
	
	return SUCCESS;
}

/*********************************************************************
*********************************************************************/
